package com.wengu.algorithm.alg;

import com.wengu.algorithm.dto.TaskAlg;
import com.wengu.algorithm.dto.UserAlg;
import com.wengu.algorithm.entity.AssignmentOutcome;
import com.wengu.algorithm.service.AssignmentOutcomeUserService;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * ETDA算法实现
 */

@Slf4j
public class ETDA extends QEA {

    // 高斯核函数的归一化常数
    private final double gaussianConst;
    // 空间索引结构，用于快速查找附近任务
//    private SpatialIndex spatialIndex;
    // 当前处理的用户引用
    public UserAlg currentUser;

    private double radius;

    /**
     * 构造函数，初始化算法参数
     * @param sigma 高斯核函数的sigma参数
     * @param alpha 权重调节参数
     */
    public ETDA(double sigma, double alpha) {
        // 参数校验
        validateParameters(sigma, alpha);
        this.sigma = sigma;
        radius = sigma;
        this.alpha = alpha;
        // 计算高斯核函数归一化常数
//        this.gaussianConst = 1.0 / (Math.sqrt(2 * Math.PI) * sigma);
        // 权重之间互相比较大小，不是计算绝对值，所以这里不需要归一化常数，直接设置为1.0，在不影响结果的情况下，可以减少计算开销
        this.gaussianConst = 1.0 ;
    }

    public ETDA(double sigma, Double alpha, Double gamma) {
        this(sigma, alpha);
        this.gamma = gamma;
    }

    /**
     * 默认构造函数，使用默认参数值
     */
//    public ETDA() {
//        this(10.0, 1);
//    }

    /**
     * 带用户参数的构造函数
     * @param user 当前用户
     */
//    public ETDA(UserAlg user) {
//        // 调用默认构造函数
//        this();
//        // 设置当前用户
//        this.currentUser = user;
//    }

    /**
     * 分配任务方法
     *
     * @param user                         当前用户
     * @param allTasks                     所有待分配任务列表
     * @param assignmentOutcome            分配结果
     * @param assignmentOutcomeUserService 用户服务
     * @param taskCacheMap                 任务缓存映射
     * @param algName                      算法名称
     * @param notIncreaseTaskIdSet
     * @return 分配的任务列表
     */
    @Override
    public List<TaskAlg> allocateTasks(UserAlg user, List<TaskAlg> allTasks, AssignmentOutcome assignmentOutcome,
                                       AssignmentOutcomeUserService assignmentOutcomeUserService,
                                       Map<Long, TaskAlg> taskCacheMap, String algName, Set<Long> notIncreaseTaskIdSet) {
        this.currentUser = user;  // 更新当前用户
        return super.allocateTasks(user, allTasks, assignmentOutcome, assignmentOutcomeUserService, taskCacheMap, algName, notIncreaseTaskIdSet);
    }

    @Override
    public List<TaskAlg> getBestPath(UserAlg user, List<TaskAlg> allTasks, Map<Long, TaskAlg> taskCacheMap, Set<Long> notIncreaseTaskIdSet) {
        this.currentUser = user;  // 更新当前用户
        return super.getBestPath(user, allTasks, taskCacheMap, notIncreaseTaskIdSet);
    }

    /**
     * 异步多线程执行
     * 分配任务方法
     *
     * @param user                         当前用户
     * @param allTasks                     所有待分配任务列表
     * @param assignmentOutcome            分配结果
     * @param assignmentOutcomeUserService 用户服务
     * @param taskCacheMap                 任务缓存映射
     * @param algName                      算法名称
     * @param notIncreaseTaskIdSet
     * @return 分配的任务列表
     */
    @Override
    public List<TaskAlg> allocateTasksAsync(UserAlg user, List<TaskAlg> allTasks, AssignmentOutcome assignmentOutcome,
                                       AssignmentOutcomeUserService assignmentOutcomeUserService,
                                       Map<Long, TaskAlg> taskCacheMap, String algName, Set<Long> notIncreaseTaskIdSet) {
        this.currentUser = user;  // 更新当前用户
        return super.allocateTasksAsync(user, allTasks, assignmentOutcome, assignmentOutcomeUserService, taskCacheMap, algName, notIncreaseTaskIdSet);
    }

    /**
     * 计算候选任务的效用值
     *
     * @param candidate            候选任务
     * @param allTasks             所有任务列表
     * @param weightCost           总成本
     * @param selectedTaskIdSet
     * @param notIncreaseTaskIdSet
     * @param deltaF
     * @param budget
     * @param usedCost
     * @return 效用值
     */
    @Override
    public double calculateUtility(TaskAlg candidate, List<TaskAlg> allTasks, double weightCost, Set<Long> selectedTaskIdSet, Set<Long> notIncreaseTaskIdSet, double deltaF, double budget, double usedCost) {
        // 如果空间索引未初始化，则创建
        if (spatialIndex == null) {
            spatialIndex = new SpatialIndex(allTasks, radius);
        }

        // 获取候选任务周围3σ半径内的任务
        List<TaskAlg> nearbyTasks = spatialIndex.getNearbyTasks(candidate.taskX, candidate.taskY, sigma);
//        log.info("任务[{}]的nearbyTasks的任务数量为：{}",candidate.id,nearbyTasks.size());

        double totalWeightedUtility = 0.000001;
        // 计算加权效用值
        for (TaskAlg nearbyTask : nearbyTasks) {
            // 如果已经选择过该任务了，则跳过
            if (selectedTaskIdSet.contains(nearbyTask.id)) {
                continue;
            }
            // 如果质量总量已经为1，则跳过
            if (notIncreaseTaskIdSet.contains(nearbyTask.id)) {
                continue;
            }
            double distance = calculateDistance(candidate.taskX, candidate.taskY, nearbyTask.taskX, nearbyTask.taskY);
            // 计算高斯权重，并乘以alpha/执行成本
//            double executionCostPow = Math.pow(nearbyTask.executionCost, alpha);
            // 候选任务的周围临近任务的权重值包括距离和执行成本的权重和
            double nearbyTaskWeightCost = distance + alpha * nearbyTask.executionCost;
            double weight = calculateGaussianWeight(nearbyTaskWeightCost);
            double nearbyTaskDeltaF = nearbyTask.calDeltaFCommon();
//            double weight = calculateGaussianWeightV2(distance,nearbyTask.executionCost);
            if (nearbyTaskDeltaF < 0) {
                log.info("DeltaF is negative: nearbyTask={},deltaF={},k={}", nearbyTask.id, deltaF,nearbyTask.k);
                notIncreaseTaskIdSet.add(nearbyTask.id);
                continue;
            }
//            double deltaFPow = Math.pow(deltaF, alpha);
//            log.info("calculateUtility: nearbyTask={},k={},deltaF={},nearbyTaskWeightCost={},weight={}",nearbyTask.id,nearbyTask.k, deltaF, nearbyTaskWeightCost,weight);
            // 对周围任务添加权重，周围的权重变小，当前任务则为1
            if (candidate.getId().equals(nearbyTask.getId())) {
                totalWeightedUtility += nearbyTaskDeltaF;
            }
            totalWeightedUtility += weight * nearbyTaskDeltaF * gamma;

        }

        return totalWeightedUtility / weightCost;  // 返回归一化的效用值
    }


    /**
     * 计算高斯权重
     * @param weightCost 距离加权执行成本
     * @return 权重值
     */
    public double calculateGaussianWeight(double weightCost) {
        double exponent = -(weightCost * weightCost) / (2 * sigma * sigma);
        return gaussianConst * Math.exp(exponent);
    }

    /**
     * 计算高斯权重
     * 包含执行成本
     *
     * @param distance      距离
     * @param executionCost
     * @return 权重值
     */
    public double calculateGaussianWeightV2(double distance, double executionCost) {
        double exponent = -(distance * distance + executionCost * executionCost) / (2 * sigma * sigma);
        return gaussianConst * Math.exp(exponent);
    }

    /**
     * 参数校验
     * @param sigma sigma参数
     * @param alpha alpha参数
     * @throws IllegalArgumentException 如果参数不合法
     */
    private void validateParameters(double sigma, double alpha) {
        if (sigma <= 0) {
            throw new IllegalArgumentException("Sigma must be positive");
        }
        if (alpha < 0) {
            throw new IllegalArgumentException("Alpha must be larger than 0");
        }
    }


    /**
     * 计算距离
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @return
     */
    public static double calculateDistanceWeight(double x1, double y1, double x2, double y2) {
        return euclidean(x1, y1, x2, y2);
    }
    /**
     * 欧拉距离
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     * @return
     */
    private static double euclidean(double x1, double y1, double x2, double y2) {
        return Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
    }
}